#pragma once
#include <iostream>
#include <array>
#include <memory>
#include <asio.hpp>
#include <sigc++/sigc++.h>

class AsyncClient : public std::enable_shared_from_this<AsyncClient> {
public:
    AsyncClient(asio::io_context& io)
        : socket_(io)
        //, work_(asio::make_work_guard(io))
    {}

//    ~AsyncClient()
//    {
//        socket_.close();
//    }

//    void stop() {
//        socket_.close();
//    }

    sigc::signal<void(const std::string&)> signal_message_received;
    sigc::signal<void()> signal_connected;

    void connect(const std::string& host, uint16_t port) {
        auto self = shared_from_this();
#if 0
        asio::ip::tcp::resolver resolver(socket_.get_executor());
        resolver.async_resolve(host, std::to_string(port),
                       [self, host, port](std::error_code ec, asio::ip::tcp::resolver::results_type endpoints) {
            std::cout << "now will emit signal_connected signal ?, ec:  " << ec << std::endl;
            std::cout << "host is " << host << ", port is " << port << std::endl;
            if (!ec) {
                std::cout << "now will emit signal_connected signal 22222, ec:  " << ec << std::endl;
                asio::async_connect(self->socket_, endpoints,
                            [self](std::error_code ec, auto) {
                    if (!ec) {
                        std::cout << "now will emit signal_connected signal" << std::endl;
                        self->signal_connected.emit();
                        self->do_read();
                    }
                });
            }
        });
#else
        auto addr = asio::ip::make_address(host);
        socket_.async_connect({addr, port} ,[self](std::error_code ec) {
            if (!ec) {
                std::cout << "now will emit signal_connected signal" << std::endl;
                self->signal_connected();
                self->do_read();
            }
        });
#endif
    }

    void send(const std::string& msg) {
        auto self = shared_from_this();
        asio::async_write(socket_, asio::buffer(msg),
                  [self](std::error_code ec, std::size_t) {
            std::cout << "in send, ec is " << ec << std::endl;
        });
    }

private:
    void do_read() {
        auto self = shared_from_this();
        socket_.async_read_some(asio::buffer(buffer_),
                    [self](std::error_code ec, std::size_t len) {
            if (!ec) {
                self->signal_message_received.emit(std::string(self->buffer_.data(), len));
                self->do_read();
            } else {
                std::cout << "in do_read, ec is " << ec << std::endl;
            }
        });
    }

    asio::ip::tcp::socket socket_;
    std::array<char, 1024> buffer_;
    //asio::executor_work_guard<asio::io_context::executor_type> work_;
};

